#ifndef PLATNETUDP
#define PLATNETUDP

#include <vector>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//DEBUGs
#include <iostream>
//Debugs

namespace Platinum{

		namespace Net{
				
				/**
				* A packet is a struct containing only 2 std::string objects which represent the IP and data recieved in a UDP packet.
				*/
				struct Packet{
						std::string IP;  ///IP Address
						std::string Data; ///Data either recieved or sent depending on whether this packet is incoming or outgoing
				};
				
				/**
				*  MakeSocket creates a UDP socket and returns the file pointer to that socket, note: the socket file pointer is an INT
				*/
				int MakeSocket(){return(socket(PF_INET, SOCK_DGRAM, 0));}
				
				namespace UDP{
						
						/**
						*	The Listener Class is literally an ear to the ground class intended for listening for incoming data
						*	It is the Listen side of a client or server object.
						*/
						class Listener{
							private:		
								int iSock; /// Socket File Descriptor
								int iPort; /// Port
								struct sockaddr_in MyAddr;    /// My address information
								struct sockaddr_in TheirAddr; /// Connector address information
								socklen_t AddrLen; ///Length of Address
								int iNumBytes; ///Number of Bytes Recieved
								char sBuf[MAXBUF];  ///Buffer to hold things in.
								
							public:
								Listener(int port) : iPort(port)
								{
									if ((iSock = MakeSocket()) == -1) {exit(1);}

									MyAddr.sin_family = AF_INET;         // host byte order
									MyAddr.sin_port = htons(iPort);     // short, network byte order
									MyAddr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
									memset(MyAddr.sin_zero, '\0', sizeof MyAddr.sin_zero);
									if (bind(iSock, (struct sockaddr *)&MyAddr, sizeof MyAddr) == -1) {exit(1);}
									#ifdef DEBUG
										std::cout << "Listener Bound to port " << iPort << std::endl;
									#endif
									
								}//End of Constructor
								
								~Listener()
								{
									close(iSock);
								}//End of Destructor
								
								/**
								*	Listen causes the Listener to block until data is recieved, 
								*	Listen returns a pointer to a Packet struct which has IP and Data members both are type string
								*	Ideally Listen should be called in a loop in a seperate thread, if you need to do anything else while waiting for Data
								*	Make sure to delete the packet returned when your are finished with it!
								*/
								Packet* Listen()
								{
									Packet* pMyPacket = new Packet;
									AddrLen = sizeof TheirAddr;
									memset(sBuf,'\0',MAXBUF); //Re-Initialize Buffer
									
									#ifdef DEBUG
										std::cout << "Listener Waiting On Packet." << std::endl; 
									#endif
									
									//This statement blocks until data is recieved at which time execution will resume.									
									if ((iNumBytes = recvfrom(iSock, &sBuf, MAXBUF-1 , 0,(struct sockaddr *)&TheirAddr, &AddrLen)) == -1)
									{
											pMyPacket->IP = "";
											pMyPacket->Data = "";
									}
									
									
									pMyPacket->IP = inet_ntoa(TheirAddr.sin_addr);
									pMyPacket->Data = sBuf;
									#ifdef DEBUG
										std::cout << "Packet Recieved From: " << pMyPacket->IP << std::endl;
										std::cout << "Packet Contains: " << pMyPacket->Data << std::endl;
									#endif
									
									return(pMyPacket);
								}
								
						};
						
						/**
						*	The Sender Class is used for sending data out over UDP
						*	It is the send side of a persistant client object
						*/
						class Sender{
							
							private:
								int iSock;
								int iPort;
								struct sockaddr_in Dest;
								std::string IP;
								/**
								*	The SendAll function ensures all data gets sent.
								*/
								int SendAll(char *buf, int len)
								{
									int total = 0;        /// how many bytes we've sent
									int bytesleft = len; /// how many we have left to send
									int n;

									while(total < len) {
										n = sendto(iSock, buf+total, bytesleft, 0, (struct sockaddr *)&Dest, sizeof Dest);
										if (n == -1) { break; }
										total += n;
										bytesleft -= n;
									}
									delete(buf);
									return n==-1?-1:0; // return -1 on failure, 0 on success
								}
								
							public:
								Sender(std::string ip,int port, int sock) : IP(ip),iPort(port), iSock(sock)
								{
									iPort = port;
									// build destination
									Dest.sin_family = AF_INET;
									inet_aton(IP.c_str(), &Dest.sin_addr);
									Dest.sin_port = htons(iPort);
									#ifdef DEBUG
										std::cout << "Sender Created for " << IP << ":" << iPort << std::endl;
									#endif
		
								}
								~Sender()
								{
									close(iSock);
								}
								
								int Send(std::string Data)
								{
									#ifdef DEBUG
										std::cout << "Sender Sending: " << Data << " to " << IP << ":" << iPort << std::endl;
									#endif
									return(SendAll(strtocharp(Data),Data.length()));
								}	
						};
				}
		}
}
#endif

